#ifndef LCD_H_
#define LCD_H_

#include <avr/io.h>
#include <util/delay.h>

#define set_bit(ADDR, BIT) (ADDR |= (1 << BIT)) 
#define clr_bit(ADDR, BIT) (ADDR &= ~(1 << BIT))

void lcd4_init();
void lcd4_clear();
void lcd4_goto(unsigned char x, unsigned char y);
void lcd4_char(unsigned char data);
void lcd4_str(char* data);
void lcd4_write(unsigned char x, unsigned char y, char* data);

//----------------

void lcd8_wait()
{
	clr_bit(DDRC, PC3);
	PORTC = _BV(PC3) | _BV(PC4);
	unsigned char end = 1;
	do
	{
		set_bit(PORTB, PB0);
		asm volatile ("nop");
		end = bit_is_set(PINC, PC3);
		clr_bit(PORTB, PB0);
	} while (end);
	set_bit(DDRC, PC3);
}

void lcd8_send(unsigned char code)
{
	set_bits(PORTC, 0xf, code >> 4);
	set_bit(PORTB, PB0);
	clr_bit(PORTB, PB0);
}

void lcd8_cmd(unsigned char cmd)
{
	lcd8_wait();
	PORTC = 0x00;
	lcd8_send(cmd);
}

void lcd4_wait()
{
	clr_bit(DDRC, PC3);
	PORTC = _BV(PC3) | _BV(PC4);
	unsigned char end = 1;
	do
	{
		set_bit(PORTB, PB0);
		asm volatile ("nop");
		end = bit_is_set(PINC, PC3);
		clr_bit(PORTB, PB0);
		set_bit(PORTB, PB0);
		clr_bit(PORTB, PB0);
	} while (end);
	set_bit(DDRC, PC3);
}

void lcd4_send(unsigned char code)
{
	set_bits(PORTC, 0x0f, code >> 4);
	set_bit(PORTB, PB0);
	clr_bit(PORTB, PB0);

	set_bits(PORTC, 0x0f, code);
	set_bit(PORTB, PB0);
	clr_bit(PORTB, PB0);
}

void lcd4_cmd(unsigned char cmd)
{
	lcd4_wait();
	PORTC = 0;
	lcd4_send(cmd);
}

void lcd4_init()
{
	DDRB |= 0x01;
	DDRC |= 0x3f;
	PORTB &= ~0x01;
	PORTC &= ~0x3f;

	lcd8_cmd(0b00100000); // 4 bit
	lcd4_cmd(0b00101000); // 2 lines, 4 bit
	lcd4_cmd(0b00001100); // Display on
	lcd4_cmd(0b00000110); // Move left on write
	lcd4_clear();
}

void lcd4_clear()
{
	lcd4_cmd(0b00000001);
}

void lcd4_goto(unsigned char x, unsigned char y)
{
	if (x >= 40 || y >= 2)
		return;
	unsigned char pos = 64 * y + x;
	lcd4_cmd(0b10000000 | (pos & 0x7f));
}

void lcd4_char(unsigned char data)
{
	lcd4_wait();
	PORTC = 0b00100000;
	lcd4_send(data);
}

void lcd4_str(char* data)
{
	for (int i = 0; data[i]; ++i)
		lcd4_char(data[i]);
}

void lcd4_write(unsigned char x, unsigned char y, char* data)
{
	lcd4_goto(x, y);
	lcd4_str(data);
}

//-----------------

#endif /* LCD_H_ */